﻿using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using System.Text;
using System;

namespace AppFuscator
{
    public class Project
    {
        [JsonProperty]
        public List<AssemblyItem> Assemblies { get; set; }

        [JsonProperty]
        public int MainAssembleIndex { get; set; }

        [JsonProperty]
        public string Version { get; set; }

        [JsonIgnore]
        public string ProjectFileName { get; set; }

        [JsonIgnore]
        public virtual AssemblyItem MainAssemble
        {
            get
            {
                if (MainAssembleIndex == -1) return null;
                return Assemblies[MainAssembleIndex];
            }
            set
            {
                if (value == null)
                    MainAssembleIndex = -1;
                else
                    MainAssembleIndex = Assemblies.IndexOf(value);
            }
        }

        [JsonIgnore]
        public virtual string ProjectDirectory
        {
            get
            {
                if (string.IsNullOrEmpty(ProjectFileName)) return null;
                return Path.GetDirectoryName(ProjectFileName);
            }
        }

        public string OutputDirectory { get; set; }

        [JsonIgnore]
        public string FullOutputPath
        {
            get
            {
                string basePath = Directory.GetCurrentDirectory();
                if (MainAssemble != null)
                    basePath = Path.GetDirectoryName(MainAssemble.FileName);
                else
                    return null;

                if (Path.IsPathRooted(OutputDirectory))
                    return OutputDirectory;
                else
                    return Path.GetFullPath(Path.Combine(basePath, OutputDirectory));
            }
        }

        public Project()
        {
            Assemblies = new List<AssemblyItem>();
            OutputDirectory = "Appfuscated";
            MainAssembleIndex = -1;
            Version = "1.0";
        }

        public virtual void Add(AssemblyItem asm)
        {
            Assemblies.Add(asm);
        }

        private void InternalDelete(AssemblyItem asm)
        {
            var id = Assemblies.IndexOf(asm);

            Assemblies.Remove(asm);
            if (MainAssembleIndex >= id)
                MainAssembleIndex--;
        }

        public virtual void Delete(AssemblyItem asm)
        {
            InternalDelete(asm);
        }

        public virtual void Delete(int id)
        {
            InternalDelete(GetItem(id));
        }

        [JsonIgnore]
        public virtual int Count
        {
            get
            {
                return Assemblies.Count;
            }
        }

        public AssemblyItem this[int id]
        {
            get
            {
                return Assemblies[id];
            }
        }

        [JsonIgnore]
        public IEnumerable<AssemblyItem> Items
        {
            get
            {
                return Assemblies.ToArray();
            }
        }
        public AssemblyItem GetItem(int i)
        {
            return Assemblies[i];
        }

        internal bool ContainsFile(string file)
        {
            foreach (var it in Assemblies)
            {
                if (Path.Equals(it.FileName, file))
                    return true;
            }

            return false;
        }

        public void Save(string filePath)
        {
            ProjectFileName = filePath;
            UpdatePaths(true);
            try
            {
                string data = Utils.SerializeObject(this);
                File.WriteAllText(filePath, data, Encoding.UTF8);
            }
            catch
            {
                System.Windows.Forms.MessageBox.Show("Can't write to: \"" + Path.GetFileName(filePath));
            }
            UpdatePaths(false);
        }

        public void SetWithUserLimits()
        {
            foreach (AssemblyItem asm in Assemblies)
            {
                asm.MergeAssemble = asm.MergeAssemble && UserTariff.Instance.AssemblyMerge;
                asm.EncryptString = asm.EncryptString && UserTariff.Instance.EncodeStrings;
                asm.HideExternalMethods = asm.HideExternalMethods && UserTariff.Instance.ExternalMethodCallHidding;
                asm.ClassDecomposition = asm.ClassDecomposition && UserTariff.Instance.Decomposition;
                asm.LibraryMode = asm.LibraryMode && UserTariff.Instance.LibraryMode;
                asm.ControlFlow = asm.ControlFlow && UserTariff.Instance.ControlFlow;
            }
        }

        public static T Load<T>(string filePath) where T : Project
        {
            string data = null;

            try
            {
                data = File.ReadAllText(filePath, Encoding.UTF8);
            }
            catch
            {
                System.Windows.Forms.MessageBox.Show("Can't open file: \"" + Path.GetFileName(filePath));
                return null;
            }

            try
            {
                var rt = Utils.DeserializeObject<T>(data);
                rt.ProjectFileName = filePath;
                rt.SetWithUserLimits();
                rt.UpdatePaths(false);
                return rt;
            }
            catch
            {
                System.Windows.Forms.MessageBox.Show("This file format is unsupported");
            }

            return null;
        }

        public IDictionary<string, string> GetAssembliesSettings()
        {
            IDictionary<string, string> dict = new Dictionary<string, string>();
        
            int i = 0;
            foreach (var sett in Assemblies)
            {
                dict.Add("asm[" + i + "][main_assembly]", sett == MainAssemble ? "true" : "false");
                dict.Add("asm[" + i + "][in_merge]", (sett != MainAssemble && sett.MergeAssemble).ToString().ToLower());
                dict.Add("asm[" + i + "][renamer]", sett.Scheme.ToString());
                dict.Add("asm[" + i + "][string_encryption]", sett.EncryptString.ToString().ToLower());
                dict.Add("asm[" + i + "][emch]", sett.HideExternalMethods.ToString().ToLower());
                dict.Add("asm[" + i + "][decomposition]", sett.ClassDecomposition.ToString().ToLower());
                dict.Add("asm[" + i + "][library_mode]", sett.LibraryMode.ToString().ToLower());
                dict.Add("asm[" + i + "][control_flow]", sett.ControlFlow.ToString().ToLower());
                i++;
            }
            return dict;
        }

        private void UpdatePaths(bool relative)
        {
            foreach (var asm in Assemblies)
            {
                if (relative)
                    asm.FileName = Utils.MakeRelativePath(ProjectDirectory, asm.FileName);
                else if (!Path.IsPathRooted(asm.FileName))
                    asm.FileName = Path.GetFullPath(Path.Combine(ProjectDirectory, asm.FileName));
            }
        }
        
    }
}
